package imse.team0.inventory.catalog;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Eugen, 4/26/12 6:40 PM
 */

/*TODO: populate the catalog file with available ids*/

public class Validator {

    private static Map<String, String> inMemoryCatalog = new HashMap<String, String>();
    private static Map<String, String> priceList = new HashMap<String, String>();
    static final String DELIM = ";";

    private Validator() {

    }

    /**
     * Return whether the item ID exists in catalog
     *
     * @param id
     * @return
     */
    private static boolean checkCatalog(String id) {
        return inMemoryCatalog.containsKey(id);
    }

    private static void start(final String ip, final int portNum) {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(Validator.class.getResourceAsStream("/catalog.txt")));
            String line;
            while ((line = br.readLine()) != null) {
                String[] tokens = line.split(DELIM);
                inMemoryCatalog.put(tokens[0], tokens[1]);
                priceList.put(tokens[0], tokens[2]);
                //System.out.println(tokens[0]);
            }
            br.close();
        } catch (IOException ex) {
            throw new RuntimeException("Exception caught in Validator constructor..\n", ex);
        }
        Thread th = new Thread(new Runnable() {
            public void run() {
                System.out.println("Catalog server loop started.");
                startListeningLoop(ip, portNum, CatalogCheckingTask.class);
            }
        });
        th.setDaemon(false);
        th.start();

        th = new Thread(new Runnable() {
            public void run() {
                System.out.println("Price list server loop started.");
                startListeningLoop(ip, portNum + 1, PriceListResponderTask.class);
            }
        });
        th.setDaemon(false);
        th.start();
    }

    private static void startListeningLoop(String ip, int portNum, Class<? extends ExecutionTask> clazz) {
        try {
            ServerSocket listenSock = new ServerSocket();
            listenSock.setReuseAddress(true);
            SocketAddress addr = new InetSocketAddress(ip, portNum);
            listenSock.bind(addr, 5);
            while (true) {
                Socket clientConn = listenSock.accept();
                try {
                    ExecutionTask task = (ExecutionTask) clazz.getConstructors()[0].newInstance(clientConn);
                    new Thread(task).start();
                } catch (Exception e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }

            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void main(String[] args) throws IOException {
        if (args.length != 2) {
            System.out.println("Must supply 2 arguments: <ip_address> <port>");
            System.exit(-1);
        }
        String ipAddr = args[0];
        int portNum = Integer.parseInt(args[1]);
        Validator.start(ipAddr, portNum);
    }

    private static abstract class ExecutionTask implements Runnable {
        protected Socket connection;

        protected ExecutionTask(Socket clientSocket) {
            connection = clientSocket;
        }
    }

    private static class CatalogCheckingTask extends ExecutionTask {
        private static final String ID_DELIM = ",";
        private static AtomicInteger req_counter = new AtomicInteger(0);
        private final String NL = System.getProperty("line.separator");

        public CatalogCheckingTask(Socket clientConn) {
            super(clientConn);
        }

        public void run() {
            try {
                BufferedReader buff = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String request = buff.readLine();
                System.out.println("InventoryCatalog:\t ("+(req_counter.incrementAndGet())+"). Received following request:(" + request + ")");
                // tokenize request
                String[] ids = request.split(ID_DELIM);
                String reply = "";
                for (String id : ids) {
                    if (!Validator.checkCatalog(id)) {
                        reply += (id + ",");
                    }
                }
                if (reply.equals("")) {
                    reply = "VALID";
                } else {
                    reply = "INVALID:" + reply.substring(0, reply.length() - 1);
                }
                Writer out = new OutputStreamWriter(connection.getOutputStream());
                out.write(reply + NL);
                out.flush();
                buff.close();
                out.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                return;
            }
        }
    }

    private static class PriceListResponderTask extends ExecutionTask {

        private static final String ID_DELIM = ",";
        private static final String PRICE_DELIM = "#";
        private final String NL = System.getProperty("line.separator");
        private static AtomicInteger req_counter = new AtomicInteger(0);

        public PriceListResponderTask(Socket clientConn) {
            super(clientConn);
        }

        public void run() {
            try {
                BufferedReader buff = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String request = buff.readLine();
                System.out.println("PriceCatalog:\t\t ("+req_counter.incrementAndGet()+"). Received following request:(" + request + ")");
                // tokenize request
                String[] ids = request.split(ID_DELIM);
                String reply = "";
                for (String id : ids) {
                    reply = reply + priceList.get(id) + PRICE_DELIM;
                }
                reply = reply.substring(0, reply.length() - 1);
                Writer out = new OutputStreamWriter(connection.getOutputStream());
                out.write(reply + NL);
                out.flush();
                buff.close();
                out.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                return;
            }
        }
    }
}